/*
 * 描          述:  <描述>
 * 修  改   人:  Administrator
 * 修改时间:  2019年6月8日
 * <修改描述:>
 */
package com.tx.core.mybatis.dao.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.mybatis.annotation.AutoSqlMap;
import com.tx.core.mybatis.assistant.StatementSqlMapBuilderAssistant;
import com.tx.core.mybatis.conditions.QuerierWrapper;
import com.tx.core.mybatis.dao.MybatisBaseDao;
import com.tx.core.mybatis.support.MyBatisDaoSupport;
import com.tx.core.paged.model.PagedList;
import com.tx.core.util.TableInfoUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 抽象持久层接口<br/>
 * //近期计划，利用Mybatis-Plus
 * <功能详细描述>
 *
 * @author Administrator
 * @version [版本号, 2019年6月8日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
public abstract class MybatisBaseDaoImpl<T, ID extends Serializable>
        implements MybatisBaseDao<T, ID>, InitializingBean {

    /** 实体类型 */
    protected final Class<T> entityType;

    /** 主键属性类型 */
    protected final Class<ID> pkPropertyType;

    /** 是否存在匹配的主键生成器 */
    protected boolean existOfPkGenerator = false;

    /** mapper助手 */
    protected StatementSqlMapBuilderAssistant assistant;

    @Autowired
    protected MyBatisDaoSupport myBatisDaoSupport;

    /** 默认的updateMapGenerator实现 */
    protected Function<T, Map<String, Object>> defaultUpdateMapGenerator;

    /** 默认的updateWrapperGenerator实现 */
    //protected Function<T, Wrapper<T>> defaultUpdateWrapperGenerator;

    /** <默认构造函数> */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public MybatisBaseDaoImpl() {
        super();
        Class<?> clazz = getClass();
        Type type = clazz.getGenericSuperclass();//getGenericSuperclass()获得带有泛型的父类
        AssertUtils.notNull(type, "type is not empty.");

        //ParameterizedType参数化类型，即泛型
        ParameterizedType p = (ParameterizedType) type;
        //getActualTypeArguments获取参数化类型的数组，泛型可能有多个
        this.entityType = (Class) p.getActualTypeArguments()[0];
        this.pkPropertyType = (Class) p.getActualTypeArguments()[1];

        init();
    }

    /**
     * 构造方法用于初始化MybatisBaseDaoImpl实例。<br/>
     *
     * @param entityType     实体类型，不能为空。
     * @param pkPropertyType 主键属性类型，不能为空。
     */
    public MybatisBaseDaoImpl(Class<T> entityType, Class<ID> pkPropertyType) {
        super();
        // 确保实体类型和主键属性类型不为空
        AssertUtils.notNull(entityType, "entityType is null.");
        AssertUtils.notNull(pkPropertyType, "pkPropertyType is null.");

        // 初始化实体类型和主键属性类型
        this.entityType = entityType;
        this.pkPropertyType = pkPropertyType;

        // 执行初始化操作
        init();
    }

    /**
     * 初始化MybatisBaseDaoImpl实例。<br/>
     */
    @Override
    public void afterPropertiesSet() {
        AssertUtils.notNull(getMyBatisDaoSupport(),
                "myBatisDaoSupport is null.");
        AssertUtils.notNull(this.entityType, "entityType is null.");
        AssertUtils.notNull(this.pkPropertyType, "pkPropertyType is null.");

        //初始化（构造函数中已初始化，这里是保证set值的情况也正确）
        init();

        this.assistant = new StatementSqlMapBuilderAssistant(
                getMyBatisDaoSupport().getConfiguration(), this.entityType);
        AssertUtils.isTrue(
                this.pkPropertyType.isAssignableFrom(
                        this.assistant.getPkColumn().getPropertyType()),
                "pkPropertyType:[{}] is not assignable from actual pkPropertyType:[{}]",
                new Object[]{this.pkPropertyType,
                        this.assistant.getPkColumn().getPropertyType()});

        //初始化Mybatis-plus中的Table-Info兼容MetaObjectHandler的实现
        TableInfoHelper.initTableInfo(this.assistant, this.entityType);

        List<TableInfoUtils.TableColumnInfo> cols = this.assistant.getTableColumns();
        List<TableInfoUtils.TableColumnInfo> cols4MapGenerator = cols.stream().filter(col -> {
            if (col.isUpdatable() || col.isPrimaryKey()) {
                return true;
            } else {
                return false;
            }
        }).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(cols4MapGenerator) || cols4MapGenerator.size() <= 1) {
            log.warn("自动生成的自动更新转换函数可能有错误.长度不对");
        }
        //生成两个默认更新函数
        this.defaultUpdateMapGenerator = new Function<T, Map<String, Object>>() {
            @Override
            public Map<String, Object> apply(T entity) {
                Map<String, Object> rowMap = new HashMap<>();
                for (TableInfoUtils.TableColumnInfo col : cols4MapGenerator) {
                    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(entity);
                    if (col.isUpdatable() || col.isPrimaryKey()) {
                        rowMap.put(col.getPropertyName(), bw.getPropertyValue(col.getPropertyName()));
                    }
                }
                return rowMap;
            }
        };
        /*this.defaultUpdateWrapperGenerator = new Function<T, Wrapper<T>>() {
            @Override
            public Wrapper<T> apply(T entity) {
                UpdateWrapper<T> wrapper = new UpdateWrapper<>(entity);
                for (TableInfoUtils.TableColumnInfo col : cols) {
                    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(entity);
                    if (col.isPrimaryKey()) {
                        wrapper.eq(col.getColumnName(), bw.getPropertyValue(col.getPropertyName()));
                    }
                    if (col.isUpdatable() && !col.isPrimaryKey()) {
                        wrapper.set(col.getColumnName(), bw.getPropertyValue(col.getPropertyName()));
                    }
                }
                return wrapper;
            }
        };*/
        //如果类上有AutoMapper或AutoMapperEntity的注解则自动注册SqlMap的实现
        if (this.entityType.isAnnotationPresent(AutoSqlMap.class)) {
            this.assistant.registe();
        }
    }

    /**
     * 初始化
     */
    private void init() {
        if (this.pkPropertyType != null) {
            this.existOfPkGenerator = String.class.isAssignableFrom(this.pkPropertyType) ||
                    Long.class.isAssignableFrom(this.pkPropertyType) ||
                    long.class.isAssignableFrom(this.pkPropertyType) ||
                    Integer.class.isAssignableFrom(this.pkPropertyType) ||
                    int.class.isAssignableFrom(this.pkPropertyType);
        }
    }

    /**
     * 获取实体类型的Class对象<br/>
     * 这个方法用于返回当前实例所操作的实体类型的Class对象。
     *
     * @return 返回实体类型的Class对象，允许泛型T通配。
     */
    protected Class<T> getEntityType() {
        return this.entityType; // 返回存储的实体类型Class对象
    }


    /**
     * 获取主键属性的类型
     * <br/>
     * 该方法用于返回当前实体类主键属性的类型。
     *
     * @return 返回主键属性的类型，类型为Class<ID>，其中ID是主键类型。
     */
    protected Class<ID> getPKPropertyType() {
        return this.pkPropertyType; // 返回主键属性的类型
    }


    /**
     * 保存或更新对象实例
     *
     * @param entity 要保存或更新的对象实例。
     */
    @Override
    public void save(T entity) {
        AssertUtils.notNull(entity, "entity is null.");

        //调用保存数据至数据库
        getMyBatisDaoSupport().save(entity, this.defaultUpdateMapGenerator, this.assistant.getFindStatementName(),
                this.assistant.getInsertStatementName(),
                this.assistant.getUpdateStatementName(),
                this.assistant.getPkColumn().getPropertyName());
    }

    /**
     * 保存或更新对象实例。该方法会根据传入的实体对象及其更新包装器生成相应的SQL语句，实现对象的保存或更新操作。
     *
     * @param entity        要保存或更新的对象实例。
     * @param updateWrapper 一个函数，用于根据对象实例生成更新条件的Wrapper。
     */
    @Override
    public void save(T entity, Wrapper<T> updateWrapper) {
        // 确保实体对象和更新包装器生成器不为空
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(updateWrapper, "updateWrapper is null.");

        // 调用MyBatis Dao支持类的save方法，执行保存或更新操作
        // 传入参数包括实体对象、更新包装器生成器、null（此处可能用于后续扩展）、以及相关的SQL语句名称（查找、插入、更新）
        getMyBatisDaoSupport().save(entity, updateWrapper, null, this.assistant.getFindStatementName(),
                this.assistant.getInsertStatementName(),
                this.assistant.getUpdateStatementName(), this.assistant.getPkColumn().getPropertyName());
    }

    /**
     * 保存或更新对象实例。该方法会根据传入的实体对象及其更新包装器生成相应的SQL语句，实现对象的保存或更新操作。
     *
     * @param entity                 要保存或更新的实体对象，不能为空。
     * @param updateWrapperGenerator 一个函数接口，用于根据实体对象生成更新操作的包装器，不能为空。
     *                               该包装器用于指定更新操作的条件和更新字段。
     */
    @Override
    public void save(T entity, Function<T, Wrapper<T>> updateWrapperGenerator) {
        // 确保实体对象和更新包装器生成器不为空
        AssertUtils.notNull(entity, "entity is null.");
        AssertUtils.notNull(updateWrapperGenerator, "updateWrapperGenerator is null.");

        // 调用MyBatis Dao支持类的save方法，执行保存或更新操作
        // 传入参数包括实体对象、更新包装器生成器、null（此处可能用于后续扩展）、以及相关的SQL语句名称（查找、插入、更新）
        getMyBatisDaoSupport().save(entity, updateWrapperGenerator, null, this.assistant.getFindStatementName(),
                this.assistant.getInsertStatementName(),
                this.assistant.getUpdateStatementName(), this.assistant.getPkColumn().getPropertyName());
    }


    /**
     * 批量保存数据至数据库<br/>
     *
     * @param entityList       需要保存的数据实体列表，不应为空
     * @param doFlushSize      执行批量保存时的刷新大小，即每次提交多少条数据到数据库
     * @param useBatchExecutor 是否使用批量执行器，true表示使用批量执行器进行保存，false则按常规方式保存
     */
    @Override
    public void batchSave(List<T> entityList, int doFlushSize, boolean useBatchExecutor) {
        if (CollectionUtils.isEmpty(entityList)) {
            // 如果传入的实体列表为空，则直接返回，不执行保存操作
            return;
        }
        // 调用批量保存方法，将实体列表保存到数据库
        getMyBatisDaoSupport().batchSave(entityList, this.defaultUpdateMapGenerator,
                this.assistant.getFindStatementName(), this.assistant.getInsertStatementName(),
                this.assistant.getUpdateStatementName(), this.assistant.getPkColumn().getPropertyName(),
                doFlushSize, useBatchExecutor);
    }


    /**
     * 批量保存对象集合<br/>
     * 对给定的对象集合进行批量保存操作。如果集合为空，则直接返回，不执行任何操作。
     * 可以通过传入的参数控制更新和插入操作的执行方式，以及批量执行的大小和是否使用批量执行器。
     *
     * @param entityList             需要批量保存的对象集合，类型为泛型T。
     * @param updateWrapperGenerator 一个函数接口，用于根据每个对象生成更新操作的Wrapper。该Wrapper用于在保存前对对象进行必要的更新操作。
     * @param doFlushSize            执行批量保存操作前，集合累积的大小。当集合大小达到该值时，将执行批量保存操作。
     * @param useBatchExecutor       是否使用批量执行器来执行保存操作。当设置为true时，将使用批量执行器来优化保存操作的性能。
     */
    @Override
    public void batchSave(List<T> entityList, Function<T, Wrapper<T>> updateWrapperGenerator, int doFlushSize,
                          boolean useBatchExecutor) {
        if (CollectionUtils.isEmpty(entityList)) {
            // 如果传入的对象集合为空，则直接返回，不执行任何操作
            return;
        }
        // 调用批量保存方法，将对象集合保存至数据库
        getMyBatisDaoSupport().batchSave(entityList, updateWrapperGenerator, null,
                this.assistant.getFindStatementName(), this.assistant.getInsertStatementName(),
                this.assistant.getUpdateStatementName(), this.assistant.getPkColumn().getPropertyName(), doFlushSize,
                useBatchExecutor);
    }


    /**
     * 插入一个实体对象到数据库。
     * 如果实体对象具有主键生成器（existOfPkGenerator为true），则使用主键生成器插入；
     * 否则，直接插入实体对象。
     *
     * @param entity 要插入的实体对象，不允许为null。
     * @throws IllegalArgumentException 如果实体对象为null，则抛出此异常。
     */
    @Override
    public void insert(T entity) {
        // 确保实体对象不为null
        AssertUtils.notNull(entity, "entity is null.");

        if (this.existOfPkGenerator) {
            // 使用主键生成器插入实体对象
            getMyBatisDaoSupport().insertWithPKGenerator(
                    this.assistant.getInsertStatementName(),
                    entity,
                    this.assistant.getPkColumn().getPropertyName());
        } else {
            // 直接插入实体对象
            getMyBatisDaoSupport()
                    .insert(this.assistant.getInsertStatementName(), entity);
        }
    }

    /**
     * 批量插入实体列表。
     * 该方法会根据实体是否存在主键生成器（PK Generator）来选择不同的批量插入策略。
     *
     * @param entityList       要插入的实体列表，不应为空。
     * @param doFlushSize      批量操作的刷新大小，即当插入实体数量达到该值时，会执行一次提交。
     * @param useBatchExecutor 是否使用批量执行器。当为true时，将使用批量执行策略进行插入。
     */
    @Override
    public void batchInsert(List<T> entityList, int doFlushSize, boolean useBatchExecutor) {
        // 检查实体列表是否为空，若为空则直接返回
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        // 当存在主键生成器时，使用特定的批量插入策略
        if (this.existOfPkGenerator) {
            getMyBatisDaoSupport().batchInsertWithPKGenerator(
                    this.assistant.getInsertStatementName(),
                    entityList,
                    this.assistant.getPkColumn().getPropertyName(),
                    doFlushSize,
                    useBatchExecutor);
        } else {
            // 不存在主键生成器时，使用默认的批量插入策略
            getMyBatisDaoSupport().batchInsert(
                    this.assistant.getInsertStatementName(),
                    entityList,
                    doFlushSize,
                    useBatchExecutor);
        }
    }

    /**
     * 根据主键删除实体。
     *
     * @param pk 主键，不能为空。
     * @return 删除是否成功。如果删除成功，返回true；否则返回false。
     */
    @Override
    public boolean delete(ID pk) {
        // 确保主键不为空
        AssertUtils.notNull(pk, "pk is null.");

        // 根据实体类型创建一个空的实体对象
        T entity = BeanUtils.instantiateClass(this.entityType);
        // 使用BeanWrapper来方便地设置和获取属性值
        BeanWrapper entityBW = PropertyAccessorFactory
                .forBeanPropertyAccess(entity);
        // 设置实体的主键值
        entityBW.setPropertyValue(
                this.assistant.getPkColumn().getPropertyName(), pk);

        // 删除实体，返回删除的行数
        int count = delete(entity);
        // 如果删除的行数小于1，表示删除失败，返回false
        if (count < 1) {
            return false;
        }
        // 断言删除的行数必须为1，否则抛出异常
        AssertUtils.isTrue(count == 1,
                "update count should == 1.but actual is :{}",
                new Object[]{count});
        return true;
    }

    /**
     * 删除指定的数据实体。
     * 本方法通过传入的数据实体对象实例，执行相应的删除操作。删除操作的具体实现依赖于MyBatis框架。
     *
     * @param entity 需要删除的对象实例。该参数是泛型T的实例，代表任意类型的数据实体。
     *               该对象实例必须不为null，否则会抛出异常。
     * @return 返回删除操作影响的行数。若影响行数大于0，则表示删除成功。
     */
    @Override
    public int delete(T entity) {
        // 确保传入的实体对象不为null
        AssertUtils.notNull(entity, "entity is null.");

        // 执行删除操作，返回删除操作影响的行数
        return getMyBatisDaoSupport()
                .delete(this.assistant.getDeleteStatementName(), entity);
    }

    /**
     * 批量删除实体列表中的所有实体。<br/>
     *
     * @param entityList       要删除的实体列表，不应为空。
     * @param doFlushSize      每执行一定数量的删除操作后刷新缓存的大小。当删除操作数量达到此值时，将刷新缓存。
     * @param useBatchExecutor 是否使用批量执行器。如果为true，则使用批量执行器来执行删除操作，可以提高性能。
     */
    @Override
    public void batchDelete(List<T> entityList, int doFlushSize, boolean useBatchExecutor) {
        // 如果传入的实体列表为空，则直接返回，不执行删除操作
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        // 调用MyBatisDaoSupport的batchDelete方法，执行批量删除
        getMyBatisDaoSupport().batchDelete(
                this.assistant.getDeleteStatementName(), entityList, doFlushSize, useBatchExecutor);
    }

    /**
     * 根据传入的主键异界Map的实例，更新对象<br/>
     *
     * @param pk              主键，不能为空
     * @param updateEntityMap 要更新的属性名和其对应的值，不能为空
     * @return 更新是否成功。成功返回true，失败返回false
     */
    @Override
    public boolean update(ID pk, Map<String, Object> updateEntityMap) {
        // 检查主键和更新实体映射不能为空
        AssertUtils.notNull(pk, "pk is null.");
        AssertUtils.notEmpty(updateEntityMap, "updateEntityMap is empty.");

        // 复制更新实体映射，并添加主键字段
        updateEntityMap = new HashMap<>(updateEntityMap);
        updateEntityMap.put(this.assistant.getPkColumn().getPropertyName(), pk);
        // 执行更新操作
        int count = getMyBatisDaoSupport().update(
                this.assistant.getUpdateStatementName(), updateEntityMap);
        // 检查更新行数，若少于1行，则更新失败
        if (count < 1) {
            return false;
        }
        // 断言更新行数正好为1行
        AssertUtils.isTrue(count == 1,
                "update count should == 1.but actual is :{}",
                new Object[]{count});
        return true;
    }

    /**
     * 根据传入的Wrapper更新对象<br/>
     * 此方法通过传入的Map对象，根据键值对的形式更新指定对象的属性值。<br/>
     * 注意：传入的Map对象必须不为空，否则会抛出异常。<br/>
     *
     * @param updateEntityMap 包含更新信息的Map对象，其中键是属性名，值是需要更新的值。此参数不能为空。
     * @return 返回更新操作影响的行数。
     */
    @Override
    public int update(Map<String, Object> updateEntityMap) {
        // 确保传入的更新信息Map对象不为空
        AssertUtils.notEmpty(updateEntityMap, "updateEntityMap is empty.");

        // 调用MyBatis DAO支持类的update方法，执行更新操作，并返回更新行数
        return getMyBatisDaoSupport().update(
                this.assistant.getUpdateStatementName(), updateEntityMap);
    }

    /**
     * 根据传入的主键和Wrapper更新对象<br/>
     *
     * @param pk      主键，用于标识要更新的对象实例。
     * @param wrapper 包装器对象，包含了更新操作所需要的条件和更新数据。
     * @return 返回更新操作是否成功。如果成功，返回true；否则返回false。
     */
    @Override
    public boolean update(ID pk, Wrapper<T> wrapper) {
        // 确保主键和wrapper不为空
        AssertUtils.notNull(pk, "pk is null.");
        AssertUtils.notNull(wrapper, "wrapper is null.");

        Map<String, Object> params = new HashMap<>();
        params.put(this.assistant.getPkColumn().getPropertyName(), pk);
        // 如果wrapper中包含实体对象，将其加入参数中
        if (wrapper.getEntity() != null) {
            params.put(Constants.ENTITY, wrapper.getEntity());
        }
        params.put(Constants.WRAPPER, wrapper);

        // 执行更新操作
        int count = getMyBatisDaoSupport().update(this.assistant.getUpdateStatementName(), params);
        // 更新成功与否的判断
        if (count < 1) {
            return false;
        }
        // 断言更新行数为1，确保更新操作的正确性
        AssertUtils.isTrue(count == 1,
                "update count should == 1.but actual is :{}",
                new Object[]{count});
        return true;
    }


    /**
     * 根据传入的Wrapper更新对象<br/>
     * 这个方法通过接收一个封装了更新操作所需信息的Wrapper对象来更新指定的对象实例。Wrapper对象中包含了要更新的对象实例和更新逻辑。<br/>
     *
     * @param wrapper 用于封装更新操作所需的信息，包括但不限于要更新的对象实例和更新逻辑。不可为null。
     * @return 返回更新操作影响的行数。
     */
    @Override
    public int update(Wrapper<T> wrapper) {
        // 确保wrapper对象不为null
        AssertUtils.notNull(wrapper, "wrapper is null.");

        // 准备用于更新操作的参数映射
        Map<String, Object> params = new HashMap<>();
        // 如果wrapper中包含要更新的实体对象，则将其添加到参数映射中
        if (wrapper.getEntity() != null) {
            params.put(Constants.ENTITY, wrapper.getEntity());
        }
        // 将wrapper对象本身也添加到参数映射中
        params.put(Constants.WRAPPER, wrapper);

        // 执行更新操作，并返回影响的行数
        return getMyBatisDaoSupport().update(this.assistant.getUpdateStatementName(), params);
    }

    /**
     * 根据主键更新对象实体。
     * 本方法首先校验传入的主键和实体对象不为null，然后将主键值设置到实体对象的主键属性上，
     * 最后通过MyBatis DAO支持类执行更新操作，并校验更新行数为1。
     *
     * @param pk     主键，不能为空。
     * @param entity 待更新的对象实例，不能为空。
     * @return boolean 更新操作是否成功。成功返回true，失败返回false。
     */
    @Override
    public boolean update(ID pk, T entity) {
        // 校验主键和实体对象不为null
        AssertUtils.notNull(pk, "pk is null.");
        AssertUtils.notNull(entity, "entity is null.");

        // 使用BeanWrapper设置实体对象的主键属性值
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(entity);
        bw.setPropertyValue(this.assistant.getPkColumn().getPropertyName(), pk);

        // 执行更新操作，并通过defaultUpdateMapGenerator生成更新映射
        int count = getMyBatisDaoSupport().update(this.assistant.getUpdateStatementName(), entity,
                this.defaultUpdateMapGenerator);
        // 更新行数小于1则失败
        if (count < 1) {
            return false;
        }
        // 校验更新行数必须为1
        AssertUtils.isTrue(count == 1,
                "update count should == 1.but actual is :{}",
                new Object[]{count});
        return true;
    }

    /**
     * 更新实体
     * <br/>
     * 根据提供的主键(pk)和实体(entity)更新数据库中的对应记录。
     *
     * @param entity 需要更新的实体对象
     * @return
     */
    @Override
    public int update(T entity) {
        AssertUtils.notNull(entity, "entity is null.");

        return getMyBatisDaoSupport().update(this.assistant.getUpdateStatementName(), entity,
                this.defaultUpdateMapGenerator);
    }

    /**
     * 根据主键 更新对象实体
     *
     * @param pk                     [主键]
     * @param entity                 [对象实例]
     * @param updateWrapperGenerator [生成更新条件函数]
     * @return [boolean]
     */
    @Override
    public boolean update(ID pk, T entity, Function<T, Wrapper<T>> updateWrapperGenerator) {
        // 确保主键和实体对象不为空
        AssertUtils.notNull(pk, "pk is null.");
        AssertUtils.notNull(entity, "entity is null.");

        // 使用BeanWrapper包装实体对象，便于操作属性
        Map<String, Object> params = new HashMap<>();
        params.put(this.assistant.getPkColumn().getPropertyName(), pk);

        // 执行更新操作
        int count = getMyBatisDaoSupport().update(this.assistant.getUpdateStatementName(), entity,
                updateWrapperGenerator, params);
        // 如果更新的记录数小于1，则认为更新失败
        if (count < 1) {
            return false;
        }
        // 断言更新的记录数必须为1，若不满足则抛出异常
        AssertUtils.isTrue(count == 1,
                "update count should == 1.but actual is :{}",
                new Object[]{count});
        return true;
    }

    /**
     * 更新实体
     *
     * @param entity                 需要更新的实体对象，不能为空。
     * @param updateWrapperGenerator 一个函数接口，用于根据实体生成更新条件的Wrapper对象。
     * @return 返回更新操作影响的行数。
     */
    @Override
    public int update(T entity, Function<T, Wrapper<T>> updateWrapperGenerator) {
        AssertUtils.notNull(entity, "entity is null."); // 确保实体对象不为空

        return getMyBatisDaoSupport().update(this.assistant.getUpdateStatementName(), entity, updateWrapperGenerator,
                null);
    }

    /**
     * 批量更新对象实例
     *
     * @param entityList       需要更新的实体列表，类型为泛型T。
     * @param doFlushSize      执行刷新的大小，即当更新操作累积到一定数量时，会执行一次数据库刷新操作。
     * @param useBatchExecutor 是否使用批量执行器。如果为true，则使用批量执行器执行更新操作。
     */
    @Override
    public void batchUpdate(List<T> entityList, int doFlushSize, boolean useBatchExecutor) {
        // 如果更新数据列表为空，则直接返回，不执行任何操作
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        this.getMyBatisDaoSupport().batchUpdate(this.assistant.getUpdateStatementName(), entityList,
                this.defaultUpdateMapGenerator, doFlushSize,
                useBatchExecutor);
    }

    /**
     * 批量更新对象实例
     *
     * @param entityList             需要更新的实体列表，类型为泛型T。
     * @param updateWrapperGenerator 一个函数接口，用于根据实体实例生成对应的更新条件包装器。接收一个实体对象，返回一个针对该实体的更新条件包装器。
     * @param doFlushSize            执行刷新的大小，即当更新操作累积到一定数量时，会执行一次数据库刷新操作。
     * @param useBatchExecutor       是否使用批量执行器来执行更新操作。如果为true，则使用批量执行策略，可以提高更新效率。
     */
    @Override
    public void batchUpdate(List<T> entityList, Function<T, Wrapper<T>> updateWrapperGenerator, int doFlushSize,
                            boolean useBatchExecutor) {
        // 如果更新数据列表为空，则直接返回，不执行任何操作
        if (CollectionUtils.isEmpty(entityList)) {
            return;
        }
        this.getMyBatisDaoSupport().batchUpdate(this.assistant.getUpdateStatementName(), entityList,
                updateWrapperGenerator, null, doFlushSize, useBatchExecutor);
    }

    /**
     * 根据主键(pk)查找实体。
     *
     * @param pk 主键, 不能为空。
     * @return 返回查询到的实体，如果不存在则返回null。
     */
    @Override
    public T find(ID pk) {
        // 确保主键不为空
        AssertUtils.notNull(pk, "pk is null.");

        // 根据实体类型创建一个空的实体对象
        T entity = BeanUtils.instantiateClass(this.entityType);
        // 获取实体的BeanWrapper, 以便于操作实体属性
        BeanWrapper entityBW = PropertyAccessorFactory
                .forBeanPropertyAccess(entity);
        // 设置实体的主键属性值
        entityBW.setPropertyValue(
                this.assistant.getPkColumn().getPropertyName(), pk);

        // 根据设置完主键值的实体对象进行查询，并返回结果
        return find(entity);
    }

    /**
     * 根据提供的实体对象查找匹配的实体。
     *
     * @param entity 要查找的实体对象, 不能为空。
     * @return 返回查询到的实体，如果不存在则返回null。
     */
    @Override
    public T find(T entity) {
        // 确保传入的实体对象不为空
        AssertUtils.notNull(entity, "entity is null.");

        // 调用MyBatis DAO支持类的find方法，执行查询，并返回结果
        return getMyBatisDaoSupport()
                .find(this.assistant.getFindStatementName(), entity);
    }

    /**
     * 查询列表方法
     * 根据提供的参数映射查询特定的数据列表。
     *
     * @param params 包含查询参数的映射对象，用于定制查询条件。
     * @return 返回查询结果列表，类型为泛型T。
     */
    @Override
    public List<T> queryList(Map<String, Object> params) {
        // 通过MyBatis DAO支持类执行查询，并返回查询结果列表
        return getMyBatisDaoSupport()
                .queryList(this.assistant.getQueryStatementName(), params);
    }


    /**
     * 根据条件查询列表方法
     * 使用提供的条件包装器和参数映射进行查询，返回符合条件的数据列表。
     *
     * @param wrapper 条件包装器，用于构建查询条件。
     * @param params  包含查询参数的映射对象，用于定制查询条件。
     * @return 返回查询结果列表，类型为泛型T。
     */
    @Override
    public List<T> queryList(Wrapper wrapper, Map<String, Object> params) {
        // 通过条件包装器和参数映射，执行查询，并返回查询结果列表
        return getMyBatisDaoSupport().queryList(
                this.assistant.getQueryStatementName(), wrapper, params);
    }


    /**
     * @param params
     * @param pageIndex
     * @param pageSize
     * @return
     */
    @Override
    public PagedList<T> queryPagedList(Map<String, Object> params,
                                       int pageIndex, int pageSize) {
        return getMyBatisDaoSupport().queryPagedList(
                this.assistant.getQueryStatementName(),
                params,
                pageIndex,
                pageSize);
    }

    /**
     * 执行分页查询操作。
     *
     * @param wrapper   用于构建查询条件的Wrapper对象，定义了查询时的筛选、排序等规则。
     * @param params    额外的查询参数，可以用于进一步筛选数据。该参数为可选，不必须。
     * @param pageIndex 请求的页码，表示需要获取第几页的数据。
     * @param pageSize  每页显示的记录数。
     * @return 返回一个PagedList对象，包含了查询结果的分页信息和数据。
     */
    @Override
    public PagedList<T> queryPagedList(Wrapper wrapper, Map<String, Object> params, int pageIndex,
                                       int pageSize) {
        // 调用getMyBatisDaoSupport().queryPagedList方法执行分页查询
        return getMyBatisDaoSupport().queryPagedList(
                this.assistant.getQueryStatementName(),
                wrapper,
                params,
                pageIndex,
                pageSize);
    }


    /**
     * 查询分页列表方法
     *
     * @param params    查询条件参数，以键值对的形式传入，用于定制查询条件。
     * @param pageIndex 请求的页码，用于指定要返回的页码。
     * @param pageSize  每页的大小，即每页返回的记录数。
     * @param count     总记录数，用于分页计算。
     * @return
     */
    @Override
    public PagedList<T> queryPagedList(Map<String, Object> params,
                                       int pageIndex, int pageSize, int count) {
        return getMyBatisDaoSupport().queryPagedList(
                this.assistant.getQueryStatementName(),
                params,
                pageIndex,
                pageSize,
                count);
    }

    /**
     * 查询分页列表方法
     *
     * @param wrapper   条件包装器，用于构建查询条件。
     * @param params    查询条件参数，以键值对的形式传入，用于定制查询条件。
     * @param pageIndex 请求的页码，用于指定要返回的页码。
     * @param pageSize  每页的大小，即每页返回的记录数。
     * @param count     总记录数，用于分页计算。
     * @return
     */
    @Override
    public PagedList<T> queryPagedList(Wrapper wrapper, Map<String, Object> params, int pageIndex,
                                       int pageSize, int count) {
        return getMyBatisDaoSupport().queryPagedList(
                this.assistant.getQueryStatementName(),
                wrapper,
                params,
                pageIndex,
                pageSize,
                count);
    }

    /**
     * 根据条件统计记录数，排除指定的ID。
     *
     * @param params  包含统计条件的参数映射
     * @param exclude 需要排除的ID，如果为空则不进行排除
     * @return 符合条件的记录数
     */
    @Override
    public int count(Map<String, Object> params, ID exclude) {
        // 初始化或复制参数映射，确保params不为空
        params = params == null ? new HashMap<String, Object>() : new HashMap<String, Object>(params);

        // 如果exclude不为空，将其添加到params中作为排除条件
        if (!ObjectUtil.isEmpty(exclude)) {
            params.put(
                    "exclude" + StringUtils.capitalize(
                            this.assistant.getPkColumn().getPropertyName()),
                    exclude);
        }

        // 调用MyBatis Dao支持类的方法，根据统计语句和参数映射获取记录数
        return getMyBatisDaoSupport()
                .count(this.assistant.getCountStatmentName(), params);
    }

    /**
     * 根据条件计算记录数，可以排除特定ID的影响。
     *
     * @param wrapper 包装器，用于构建查询条件，可以为null，若为null则构建一个空的查询条件。
     * @param params  附加参数，可用于查询条件的构建或其它用途。
     * @param exclude 需要排除的ID，如果存在，将在查询条件中添加此ID的排除条件。
     * @return 符合条件的记录数。
     */
    @Override
    public int count(Wrapper wrapper, Map<String, Object> params, ID exclude) {
        // 如果wrapper为null，则构建一个空的wrapper
        wrapper = wrapper == null ? QuerierWrapper.<T>builder().build()
                : wrapper;
        // 确保wrapper是AbstractWrapper的实例
        AssertUtils.isInstanceOf(AbstractWrapper.class, wrapper, "wrapper应该是AbstractWrapper的实例");

        // 类型转换，以便使用AbstractWrapper的方法
        AbstractWrapper ew = (AbstractWrapper) wrapper;
        // 如果exclude不为空，添加排除条件
        if (!ObjectUtil.isEmpty(exclude)) {
            ew.ne(this.assistant.getPkColumn().getPropertyName(), exclude);
        }

        // 调用MyBatis Dao支持类计算记录数
        return getMyBatisDaoSupport().count(
                this.assistant.getCountStatmentName(), ew, params);
    }


    /**
     * @return 返回 myBatisDaoSupport
     */
    public MyBatisDaoSupport getMyBatisDaoSupport() {
        return this.myBatisDaoSupport;
    }

    public void setMyBatisDaoSupport(MyBatisDaoSupport myBatisDaoSupport) {
        this.myBatisDaoSupport = myBatisDaoSupport;
    }

    public StatementSqlMapBuilderAssistant getAssistant() {
        return assistant;
    }
}
